/*
    作者：zx
    时间：2022.11.7
    描述：32位保护模式下读取kernel程序，解析并跳转执行
*/

#include "loader.h"

static void read_disk(uint32_t sector, uint16_t sector_count, uint8_t * buff)
{   //读取kernel elf文件
    outb(0x1F6,0xE0);
    outb(0x1F2,(uint8_t)(sector_count>>8));
    outb(0x1F3,(uint8_t)(sector>>24));
    outb(0x1F4,0);
    outb(0x1F5,0);

    outb(0x1F2,(uint8_t)(sector_count));
    outb(0x1F3,(uint8_t)(sector));
    outb(0x1F4,(uint8_t)(sector>>8));
    outb(0x1F5,(uint8_t)(sector>>16));

    outb(0x1F7,0x24);

    uint16_t * data_buff = (uint16_t *)buff;
    while(sector_count--)
    {
        while( (inb(0x1F7)&0x88) != 0x8) {}

        for(int i=0;i<SECTOR_SIZE/2;i++)
        {
            *data_buff = inw(0x1F0);
            data_buff++;
        }
    }
}


uint32_t reload_elf_file(uint8_t * file_buffer)
{   
    // 解析elf文件
    Elf32_Ehdr * elf_hdr = (Elf32_Ehdr *)file_buffer;
    // elf文件检查
    if(elf_hdr->e_ident[0]!=0x7f || elf_hdr->e_ident[1]!= 'E' || 
        elf_hdr->e_ident[2]!='L' || elf_hdr->e_ident[3]!='F')
    {
        return 0;
    }

    // elf文件程序段读取
    for(int i=0;i<elf_hdr->e_phnum;i++)
    {
        Elf32_Phdr * phdr = (Elf32_Phdr *)(file_buffer + elf_hdr->e_phoff) + i;
        if(phdr->p_type != PT_LOAD) continue;

        uint8_t * src = file_buffer + phdr->p_offset;
        uint8_t * dest = (uint8_t *)phdr->p_paddr;
        for(int j=0;j<phdr->p_filesz;j++)
        {
            *dest++ = *src++;
        }

        dest = (uint8_t *)phdr->p_paddr + phdr->p_filesz;
        // dest = (uint8_t *)file_buffer + phdr->p_filesz;
        for(int j=0;j < phdr->p_memsz - phdr->p_filesz; j++)
        {
            *dest++ = 0;
        }
    }
    
    return elf_hdr->e_entry;
}

static void die(int code)
{
    for(;;){}
}

// 此两位开启分页机制
#define CR4_PSE (1<<4)
#define CR0_PG (1<<31)

// 页表项 32位每一位的作用
// 页表是否存在
#define PDE_P (1<<0)
// 对应的页是否可写
#define PDE_W (1<<1)
// 对应页的大小 1表示一页4MB
#define PDE_PS (1<<7)
// 


// 开启分页机制，整个分页机制只有一级页表，逻辑地址= 页号10位+页内偏移22位，一页4MB
// 因为需要先进入kernel 所以先只定义一个4MB的内存区域
void enable_page_mode(void)
{
    // 起始地址是4kb的倍数 即最小的12位是0
    // 因为页表起始地址需要放到cr3寄存器，这个寄存器0-11位不是地址 有其他用
    // 每一个页表项最高10位代表页号，用来索引内存页
    // 其他22位各有作用
    static uint32_t page_dir[1024] __attribute__((aligned(4096))) = {
        [0] = PDE_P | PDE_W | PDE_PS,
    };

    uint32_t cr4 = read_cr4();
    write_cr4(cr4 | CR4_PSE);
    write_cr3((uint32_t)page_dir);
    write_cr0(read_cr0() | CR0_PG);
}

void load_kernel(void)
{
    read_disk(100,500,(uint8_t *)SYS_KERNEL_LOAD_ADDR);
    enable_page_mode();
    uint32_t kernel_entry = reload_elf_file((uint8_t *)SYS_KERNEL_LOAD_ADDR);
    if(kernel_entry == 0) die(-1);
    ((void (*)(boot_info_t *))kernel_entry)(&boot_info);
}